///###############
//FILE          : FamilyTree.java
//WRITER        : Lior Zimmerman, liorz06, 066462300
//WRITER        : Ohad Dan, ohad_dan, 036767960
//EXERCISE      : oop ex1 2008
//DESCRIPTION   : Implements a family tree with a sorted structure
//###############
package oop.ex1.family;
import java.util.List;
import java.util.ArrayList;

import oop.ex1.sortedStructure.SortedStructureException;

/**
 * Family member list, held sorted. Manage all family tree functions,
 * keeping it sorted in all time. 
 */
public class FamilyTree{
	
	//*****************************************************
	// Data Member :                                      * 
	//*****************************************************
	oop.ex1.sortedStructure.SortedStructure
	<String, FamilyMember> _ftree;
	
	//*****************************************************
	// Methods     :                                      * 
	//*****************************************************
	/**
	 * Construct a new family tree.
	 */
	public FamilyTree( oop.ex1.sortedStructure.SortedStructureFactory
		   <String, FamilyMember> factory){
	   _ftree = factory.getInstance();
   }
	/**
	 * Is the tree empty.
	 * @return True if there are no family members  in the tree,
	 * false otherwise.
	 */
	public Boolean isEmpty(){ 
		return _ftree.isEmpty();
	}
	/**
	 * Returns the size of the family tree.
	 * @return Number of family members in the tree.
	 */
	public int size(){
		return _ftree.size();
	}
	/**
	 * Returns the given, by name, family member.
	 * @param name The name of the desired family member.
	 * @return The desired family member.
	 */
	public FamilyMember get(String name){ 
		return _ftree.get(name);
	}
	/**
	 * Inserts a new family member to the  family tree.
	 * @param person Family member to be added.
	 * @throws FamilyException Case of a null person.
	 */
	public void insert(FamilyMember person) throws FamilyException{
		if ( person == null ){
			throw new FamilyException( "can't insert null person");
		}
		
		try{
		    _ftree.insert(person);
		}
		catch( SortedStructureException e ){
		    throw new FamilyException( "Error Adding person to family tree");
		}
	}
	/**
	 * Removes a person from the list.
	 * @param name Name of the person to be removed.
	 * @return The removed family member.
	 */
	public FamilyMember remove (String name){ 
		return _ftree.remove( name );
	}  
	/**
	 * Returns all the keys of family members in the family tree.
	 * @return Keys of all family members in the family tree.
	 */
	public List<String> getAllKeys() { 
		return _ftree.getAllKeys();
	}
	/**
	 * Prints the lineage of the given person.
	 * @param name Name of the family member whose lineage will 
	 * be printed.
	 * @throws FamilyException case of a null family member.
	 */
	public void printLineage(String name) throws FamilyException {
	    if ( _ftree.get(name) == null ) {
			throw new FamilyException("Name not found");
		}
		prettyPrintLineage(name,0);
	}
	/**
	 * Reursivley prints the lineage of the given person.
	 * @param name Name of the family member whose lineage will 
	 * be printed.
	 * @param spaces Current spaces to be printed, according to
	 * place in lineage.
	 */
	private void prettyPrintLineage(String name, int spaces) {
	    for ( int i=0; i<spaces; i++ ){
			System.out.print(" ");
		}
		FamilyMember person = _ftree.get(name);
		if ( person == null ) {
		    return;
		}
		System.out.println( person );
		if ( person.getChildren().size() == 0 ) {
			return;
		}
		//Recursivley prints the children lineage.
		List<FamilyMember> children = person.getChildren();
		for ( FamilyMember kid:children ){
			prettyPrintLineage( kid.getName(), spaces+3 );
		}
	}
	/**
	 * Returns a list of all the elders (those whose parents are unknown)
	 * in the family tree.
	 * @return A list with all the elders in the family tree.
	 */
	public List<FamilyMember> findElders(){ 
		List<FamilyMember> elders = new ArrayList<FamilyMember>();
		List<String> names = _ftree.getAllKeys();
		if ( names == null ) {
		    return null;
		}
		for( String name:names ){
			if ( _ftree.get(name).getMother() == null && 
					_ftree.get(name).getFather() == null ) {
				elders.add( _ftree.get(name) );
			}
		}
		return elders;
	}

}